// kernel.cc 
//	Initialization and cleanup routines for the Nachos kernel.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "debug.h"
#include "main.h"
#include "kernel.h"
#include "sysdep.h"
#include "synch.h"
#include "synchlist.h"
#include "libtest.h"
#include "string.h"
#include "synchconsole.h"
#include "synchdisk.h"
#include "post.h"
#include "bitmap.h"
#include <sstream>
#include <iostream>
#include <string>
using namespace std;

//----------------------------------------------------------------------
// Kernel::Kernel
// 	Interpret command line arguments in order to determine flags 
//	for the initialization (see also comments in main.cc)  
//----------------------------------------------------------------------


Kernel::Kernel(int argc, char **argv)
{
    randomSlice = FALSE; 
    debugUserProg = FALSE;
    consoleIn = NULL;          // default is stdin
    consoleOut = NULL;         // default is stdout
#ifndef FILESYS_STUB
    formatFlag = FALSE;
#endif
    reliability = 1;            // network reliability, default is 1.0
    hostName = 0;               // machine id, also UNIX socket name
                                // 0 is the default machine id

    Mmbmp = new Bitmap(NumPhysPages);
    lstaddrspace = new List<AddrSpace *>;

    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-rs") == 0) {
 	    ASSERT(i + 1 < argc);
	    RandomInit(atoi(argv[i + 1]));// initialize pseudo-random
					// number generator
	    randomSlice = TRUE;
	    i++;
        } else if (strcmp(argv[i], "-s") == 0) {
            debugUserProg = TRUE;
	} else if (strcmp(argv[i], "-ci") == 0) {
	    ASSERT(i + 1 < argc);
	    consoleIn = argv[i + 1];
	    i++;
	} else if (strcmp(argv[i], "-co") == 0) {
	    ASSERT(i + 1 < argc);
	    consoleOut = argv[i + 1];
	    i++;
#ifndef FILESYS_STUB
	} else if (strcmp(argv[i], "-f") == 0) {
	    formatFlag = TRUE;
#endif
        } else if (strcmp(argv[i], "-n") == 0) {
            ASSERT(i + 1 < argc);   // next argument is float
            reliability = atof(argv[i + 1]);
            i++;
        } else if (strcmp(argv[i], "-m") == 0) {
            ASSERT(i + 1 < argc);   // next argument is int
            hostName = atoi(argv[i + 1]);
            i++;
        } else if (strcmp(argv[i], "-u") == 0) {
            cout << "Partial usage: nachos [-rs randomSeed]\n";
	    cout << "Partial usage: nachos [-s]\n";
            cout << "Partial usage: nachos [-ci consoleIn] [-co consoleOut]\n";
#ifndef FILESYS_STUB
	    cout << "Partial usage: nachos [-nf]\n";
#endif
            cout << "Partial usage: nachos [-n #] [-m #]\n";
	}
    }
}

//----------------------------------------------------------------------
// Kernel::Initialize
// 	Initialize Nachos global data structures.  Separate from the 
//	constructor because some of these refer to earlier initialized
//	data via the "kernel" global variable.
//----------------------------------------------------------------------

void
Kernel::Initialize()
{
    // We didn't explicitly allocate the current thread we are running in.
    // But if it ever tries to give up the CPU, we better have a Thread
    // object to save its state. 
    currentThread = new Thread("main");		
    currentThread->setStatus(RUNNING);

    stats = new Statistics();		// collect statistics
    interrupt = new Interrupt;		// start up interrupt handling
    scheduler = new Scheduler();	// initialize the ready queue
    alarm = new Alarm(randomSlice);	// start up time slicing
    machine = new Machine(debugUserProg);
    synchConsoleIn = new SynchConsoleInput(consoleIn); // input from stdin
    synchConsoleOut = new SynchConsoleOutput(consoleOut); // output to stdout
    synchDisk = new SynchDisk();    //
#ifdef FILESYS_STUB
    fileSystem = new FileSystem();
#else
    fileSystem = new FileSystem(formatFlag);
#endif // FILESYS_STUB
    postOfficeIn = new PostOfficeInput(10);
    postOfficeOut = new PostOfficeOutput(reliability);

    interrupt->Enable();
}

//----------------------------------------------------------------------
// Kernel::~Kernel
// 	Nachos is halting.  De-allocate global data structures.
//----------------------------------------------------------------------

Kernel::~Kernel()
{
    delete stats;
    delete interrupt;
    delete scheduler;
    delete alarm;
    delete machine;
    delete synchConsoleIn;
    delete synchConsoleOut;
    delete synchDisk;
    delete fileSystem;
    delete postOfficeIn;
    delete postOfficeOut;
    
    Exit(0);
}

//----------------------------------------------------------------------
// Kernel::ThreadSelfTest
//      Test threads, semaphores, synchlists
//----------------------------------------------------------------------

void
Kernel::ThreadSelfTest() {
   Semaphore *semaphore;
   SynchList<int> *synchList;
   
   LibSelfTest();		// test library routines
   
   currentThread->SelfTest();	// test thread switching
   
   				// test semaphore operation
   semaphore = new Semaphore("test", 0);
   semaphore->SelfTest();
   delete semaphore;
   
   				// test locks, condition variables
				// using synchronized lists
   synchList = new SynchList<int>;
   synchList->SelfTest(9);
   delete synchList;

}

void
Kernel::schedulingTest(char *filename)
{
	Semaphore *semaphore;
	SynchList<int> *synchList;

	LibSelfTest();		// test library routines

	currentThread->SchedulingTest(filename);	// test thread switching

				// test semaphore operation
	semaphore = new Semaphore("test", 0);
	semaphore->SelfTest();
	delete semaphore;

				// test locks, condition variables
				// using synchronized lists
	synchList = new SynchList<int>;
	synchList->SelfTest(9);
	delete synchList;
}


void
Kernel::synchTest()
{
	Semaphore *semaphore;
	SynchList<int> *synchList;

	// test semaphore operation
	semaphore = new Semaphore("test", 0);
	semaphore->synchTest();
	delete semaphore;

				// test locks, condition variables
				// using synchronized lists
	synchList = new SynchList<int>;
	synchList->SelfTest(9);
	delete synchList;
}
//----------------------------------------------------------------------
// Kernel::ConsoleTest
//      Test the synchconsole
//----------------------------------------------------------------------

void
Kernel::ConsoleTest() {
    char ch;

    cout << "Testing the console device.\n" 
        << "Typed characters will be echoed, until ^D is typed.\n"
        << "Note newlines are needed to flush input through UNIX.\n";
    cout.flush();

    do {
        ch = synchConsoleIn->GetChar();
        if(ch != EOF) synchConsoleOut->PutChar(ch);   // echo it!
    } while (ch != EOF);

    cout << "\n";

}

//----------------------------------------------------------------------
// Kernel::NetworkTest
//      Test whether the post office is working. On machines #0 and #1, do:
//
//      1. send a message to the other machine at mail box #0
//      2. wait for the other machine's message to arrive (in our mailbox #0)
//      3. send an acknowledgment for the other machine's message
//      4. wait for an acknowledgement from the other machine to our 
//          original message
//
//  This test works best if each Nachos machine has its own window
//----------------------------------------------------------------------

void
Kernel::NetworkTest() {

    if (hostName == 0 || hostName == 1) {
        // if we're machine 1, send to 0 and vice versa
        int farHost = (hostName == 0 ? 1 : 0); 
        PacketHeader outPktHdr, inPktHdr;
        MailHeader outMailHdr, inMailHdr;
        char *data = "Hello there!";
        char *ack = "Got it!";
        char buffer[MaxMailSize];

        // construct packet, mail header for original message
        // To: destination machine, mailbox 0
        // From: our machine, reply to: mailbox 1
        outPktHdr.to = farHost;         
        outMailHdr.to = 0;
        outMailHdr.from = 1;
        outMailHdr.length = strlen(data) + 1;

        // Send the first message
        postOfficeOut->Send(outPktHdr, outMailHdr, data); 

        // Wait for the first message from the other machine
        postOfficeIn->Receive(0, &inPktHdr, &inMailHdr, buffer);
        cout << "Got: " << buffer << " : from " << inPktHdr.from << ", box " 
                                                << inMailHdr.from << "\n";
        cout.flush();

        // Send acknowledgement to the other machine (using "reply to" mailbox
        // in the message that just arrived
        outPktHdr.to = inPktHdr.from;
        outMailHdr.to = inMailHdr.from;
        outMailHdr.length = strlen(ack) + 1;
        postOfficeOut->Send(outPktHdr, outMailHdr, ack); 

        // Wait for the ack from the other machine to the first message we sent
	postOfficeIn->Receive(1, &inPktHdr, &inMailHdr, buffer);
        cout << "Got: " << buffer << " : from " << inPktHdr.from << ", box " 
                                                << inMailHdr.from << "\n";
        cout.flush();
    }

    // Then we're done!
}

void
Kernel::Shell()
{
	stringstream strStream;
	string totalLine;
	string tmpPara;
	char arg1[10];
	char arg2[10];


	while(true)
	{
		arg1[0] = '\0';
		arg2[0] = '\0';
		strStream.clear();
		tmpPara.clear();
		totalLine.clear();
		paraArray.clear();
		cout<<"bcg@Nachos:"<<kernel->fileSystem->Pwd()<<"# ";
		getline(cin,totalLine);

		strStream << totalLine;

		while(true)
		{
			strStream >> tmpPara;
			paraArray.push_back(tmpPara);

			if (strStream.eof())
			{
				break;
			}
		}

		list<string>::iterator iter=paraArray.begin();

		for (;(iter!=paraArray.end());iter++)
		{
			if ("fuck" == *iter)
			{
				cout<<"Fuck!!"<<endl;
				break;
			}

			if ("echo" == *iter)
			{
				if ((++iter) != paraArray.end())
				{
					strcpy(arg1,(*iter).c_str());
					cout<<totalLine.substr(5)<<endl;
				}
				else
				{
					cout<<"Oh,brother,what do you want to ECHO?"<<endl;
				}
				break;
			}

			if ("format" == *iter)
			{
				kernel->fileSystem = new FileSystem(TRUE);
				break;
			}

			if ("pwd" == *iter)
			{
				cout<<kernel->fileSystem->Pwd()<<endl;
				break;
			}

			if ("ls" == *iter)
			{
				if ((++iter) != paraArray.end())
				{
					strcpy(arg1,(*iter).c_str());
					kernel->fileSystem->List(arg1);
				}
				else
					kernel->fileSystem->List();
				break;
			}

			if ("cp" == *iter)
			{
				if ((++iter) != paraArray.end())
				{
					strcpy(arg1,(*iter).c_str());
				}
				else
				{
					cout<<"Oh,you even don't give me one parameter!!"<<endl;
					break;
				}

				if ((++iter) != paraArray.end())
				{
					strcpy(arg2,(*iter).c_str());
					kernel->fileSystem->CopyFileFromUnix(arg1,arg2);
				}
				else
				{
					cout<<"Oh,parameter is not enough!!"<<endl;
				}

				break;
			}

			if ("ap" == *iter)
			{
				if ((++iter) != paraArray.end())
				{
					strcpy(arg1,(*iter).c_str());
				}
				else
				{
					cout<<"Oh,you even don't give me one parameter!!"<<endl;
					break;
				}

				if ((++iter) != paraArray.end())
				{
					strcpy(arg2,(*iter).c_str());
					kernel->fileSystem->AppendFileFromUnix(arg1,arg2);
				}
				else
				{
					cout<<"Oh,parameter is not enough!!"<<endl;
				}

				break;
			}

			if ("apn" == *iter)
			{
				if ((++iter) != paraArray.end())
				{
					strcpy(arg1,(*iter).c_str());
				}
				else
				{
					cout<<"Oh,you even don't give me one parameter!!"<<endl;
					break;
				}

				if ((++iter) != paraArray.end())
				{
					strcpy(arg2,(*iter).c_str());
					kernel->fileSystem->AppendFileFromNachos(arg1,arg2);
				}
				else
				{
					cout<<"Oh,parameter is not enough!!"<<endl;
				}

				break;
			}

			if ("touch" == *iter)
			{
				if ((++iter) != paraArray.end())
				{
					strcpy(arg1,(*iter).c_str());
					kernel->fileSystem->Create(arg1,0);
				}
				else
				{
					cout<<"Oh,brother,you need to give a filename to create!"<<endl;
				}
				break;
			}

			if ("mv" == *iter)
			{
				if ((++iter) != paraArray.end())
				{
					strcpy(arg1,(*iter).c_str());
				}
				else
				{
					cout<<"Oh,you even don't give me one parameter!!"<<endl;
					break;
				}

				if ((++iter) != paraArray.end())
				{
					strcpy(arg2,(*iter).c_str());
					kernel->fileSystem->ChangefileName(arg1,arg2);
				}
				else
				{
					cout<<"Oh,parameter is not enough!!"<<endl;
				}

				break;
			}

			if ("cpn" == *iter)
			{
				if ((++iter) != paraArray.end())
				{
					strcpy(arg1,(*iter).c_str());
				}
				else
				{
					cout<<"Oh,you even don't give me one parameter!!"<<endl;
					break;
				}

				if ((++iter) != paraArray.end())
				{
					strcpy(arg2,(*iter).c_str());
					kernel->fileSystem->NachosfileCopy(arg1,arg2);
				}
				else
				{
					cout<<"Oh,parameter is not enough!!"<<endl;
				}

				break;
			}

			if ("rm" == *iter)
			{
				if ((++iter) != paraArray.end())
				{
					strcpy(arg1,(*iter).c_str());
					kernel->fileSystem->Remove(arg1);
				}
				else
				{
					cout<<"Oh,brother,you need to give a filename to delete"<<endl;
				}
				break;
			}

			if ("mkdir" == *iter)
			{
				if ((++iter) != paraArray.end())
				{
					strcpy(arg1,(*iter).c_str());
					kernel->fileSystem->Mkdir(arg1);
				}
				else
				{
					cout<<"Oh,brother,you need to give a directory name!!"<<endl;
				}
				break;
			}

			if ("rmdir" == *iter)
			{
				if ((++iter) != paraArray.end())
				{
					strcpy(arg1,(*iter).c_str());
					kernel->fileSystem->Rmdir(arg1);
				}
				else
				{
					cout<<"Oh,brother,you need to give a directory name!!"<<endl;
				}
				break;
			}

			if ("cd" == *iter)
			{
				if ((++iter) != paraArray.end())
				{
					strcpy(arg1,(*iter).c_str());
					kernel->fileSystem->Cd(arg1);
				}
				else
				{
					cout<<"Oh,brother,you need to give a directory name!!"<<endl;
				}
				break;
			}

			if ("pushd" == *iter)
			{
				if ((++iter) != paraArray.end())
				{
					strcpy(arg1,(*iter).c_str());
					kernel->fileSystem->Pushd(arg1);
				}
				else
				{
					cout<<"Oh,brother,you need to specify a directory filename !"<<endl;
				}
				break;
			}

			if ("popd" == *iter)
			{
				kernel->fileSystem->Popd();
				break;
			}

			if ("cat" == *iter || "print" == *iter)
			{
				if ((++iter) != paraArray.end())
				{
					strcpy(arg1,(*iter).c_str());
					kernel->fileSystem->PrintFile(arg1);
				}
				else
				{
					cout<<"Oh,brother,you need to give a filename to print"<<endl;
				}
				break;
			}

			if ("disk" == *iter)
			{
				kernel->fileSystem->Print();
				break;
			}

			if ("exit" == *iter)
			{
				cout << "Machine halting!\n\n";
				kernel->stats->Print();
				delete kernel;
			}
			if ("help" == *iter)
			{
				cout<<"Welcome to Nachos Shell!"<<endl;
				cout<<"Group Members:Baoli,Tankery,GongXiaocong@SeedClass2008"<<endl;
				cout<<"Version 0.05	\n"<<endl;
				cout<<"Available Commands:\n"
					  "		help		Show Nachos Shell Command and version\n"
					  "		format  	format nachos file system\n"
					  "		touch <nachos file>\n"
					  "				create a nachos file\n"
					  "		cp <unix file> <nachos file>\n"
					  "		cpn <nachos file 1> <nachos file 2>\n"
					  "				copy nachos file 1 to nachos file 2 \n"
					  "		mv <nachos file 1> <nachos file 2>\n"
					  "				rename  nachos file 1 to nachos file 2 \n"
					  "		ap <unix file> <nachos file>\n"
					  "				append unix file to nachos file\n"
					  "		apn <nachos file 1> <nachos file 2>\n"
					  "				append nachos file 1 to nachos file 2\n"
					  "		rm <nachos file>\n"
					  "				delete file from nachos file system\n"
					  "		mkdir <nachos dirname>\n"
					  "				create nachos directory \n"
					  "		rmdir <nachos dirname>\n"
					  "				delete directory from nachos file system\n"
					  "		cd <nachos dirname>\n"
					  "				enter nachos directory \n"
					  "		print/cat <machos file>\n"
					  "				print content of a nachos file\n"
					  "		ls			list nachos files in current directory\n"
					  "		disk		print the nachos \"DISK\"\n"
					  "		exit		exit from nachos shell\n"
					  "		pwd		print name of current/working directory\n"
					  "		pushd <dirname>\n"
					  "				Save and then change current directory to dirname\n"
					  "		popd		change current directory to the top dir of the directory stack\n"
					  "		echo		display a line of text\n"
					<<endl;
				break;
			}
			else
			{
				cout<<"Command Not Found!"<<endl;
			}
		}
	}
}

